package frsf.cidisi.exercise.laberinto.search;

import frsf.cidisi.exercise.laberinto.search.util.Celda;
import frsf.cidisi.exercise.laberinto.search.util.Entorno;
import frsf.cidisi.exercise.laberinto.search.util.Orientacion;
import frsf.cidisi.exercise.laberinto.search.util.Par;
import frsf.cidisi.faia.agent.Perception;
import frsf.cidisi.faia.agent.search.SearchBasedAgentState;

/**
 * Represent the internal state of the Agent.
 */
public class EstadoAgente extends SearchBasedAgentState {
	
	private Par posicion;
	private Orientacion orientacion;
    private boolean poseeLlave;
    private Entorno entorno;
	
	public EstadoAgente() {
        this.initState();
    }
    
    /**
     * This method is optional, and sets the initial state of the agent.
     */
    @Override
    public void initState() {
    	posicion = null;
    	orientacion = new Orientacion();
		poseeLlave = false;
		entorno = null;
    }
    
    public EstadoAgente(Par posicion, Orientacion orientacion, boolean poseeLlave, 
            			Entorno entorno) {
    	super();
    	this.posicion = posicion;
    	this.orientacion = orientacion;
    	this.poseeLlave = poseeLlave;
    	this.entorno = entorno;
    }
    
    /**
     * This method is used to update the Agent State when a Perception is
     * received by the Simulator.
     */
    @Override
    public void updateState(Perception p) {
        
        //TODO: Complete Method
    	AgentePerception percepcion = (AgentePerception) p;
    	Entorno e = percepcion.getEntorno();
    	this.setEntorno(e);
    	
    	if(posicion == null)
    		posicion = entorno.getEntradas().get(0);
    	
    	if(!this.poseeLlave() && this.encontroLlave())
    		this.setPoseeLlave(true);
    }

    //TODO: Complete this section with agent-specific methods
    // The following methods are agent-specific:
   	
	public Par getPosicion() {
		return posicion;
	}

	public void setPosicion(Par posicion) {
		this.posicion = posicion;
		if(!this.poseeLlave() && this.encontroLlave())
    		this.setPoseeLlave(true);
	}

	public Orientacion getOrientacion() {
		return orientacion;
	}

	public void setOrientacion(Orientacion orientacion) {
		this.orientacion = orientacion;
	}

	public boolean poseeLlave() {
		return poseeLlave;
	}
	
	public void setPoseeLlave(boolean poseeLlave) {
		this.poseeLlave = poseeLlave;
	}
	
	public Entorno getEntorno() {
		return entorno;
	}

	public void setEntorno(Entorno entorno) {
		this.entorno = entorno;
	}

	public boolean puedeAvanzar() {
		int x = posicion.getX();
		int y = posicion.getY();
//		System.out.print("x: " + x +" y: " + y + " o: " + orientacion);
		Celda celda = entorno.getLaberinto()[x][y];
		int direccion = orientacion.getDireccion();
//		System.out.println(" permite: " + celda.estaLibre(direccion));
		return celda.estaLibre(direccion);
	}
/*	
	public void avanzar() {
        Par orient = orientacion.getOrientacion();
        Par nuevaPos = posicion.sumar(orient);
        this.setPosicion(nuevaPos);
	}
	
	public void girarDerecha() {
		orientacion.girarDerecha();        
	}
	
	public void girarIzquierda() {
		orientacion.girarIzquierda();        
	}*/
		
	public boolean encontroSalida() {
		return entorno.getSalidas().contains(posicion);
	}
	
	public boolean encontroCerrojo() {
		return entorno.getCerrojos().contains(posicion);
	}
	
	public boolean encontroLlave() {
		return entorno.getLlaves().contains(posicion);
	}
	
	public boolean orientacionDeSalida() {
		return orientacion.orientacionDeSalida();
	}
	
    /**
     * This method is used in the search process to verify if the node already
     * exists in the actual search.
     */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (getClass() != obj.getClass())
			return false;
		EstadoAgente other = (EstadoAgente) obj;
		if (entorno == null) {
			if (other.entorno != null)
				return false;
		} else if (!entorno.equals(other.entorno))
			return false;
		if (orientacion == null) {
			if (other.orientacion != null)
				return false;
		} else if (!orientacion.equals(other.orientacion))
			return false;
		if (poseeLlave != other.poseeLlave)
			return false;
		if (posicion == null) {
			if (other.posicion != null)
				return false;
		} else if (!posicion.equals(other.posicion))
			return false;
		return true;
	}
	
    /**
     * This method clones the state of the agent. It's used in the search
     * process, when creating the search tree.
     */
	@Override
    public SearchBasedAgentState clone() {
		if (posicion != null)
			return new EstadoAgente((Par) posicion.clone(),	(Orientacion) orientacion.clone(),
				                poseeLlave, (Entorno) entorno.clone());
		else
			return new EstadoAgente();
    }
	
    /**
     * This method returns the String representation of the agent state.
     */
	@Override
	public String toString() {
		return "EstadoAgente [entorno=" + entorno + ", orientacion=" + orientacion + 
		       ", poseeLlave=" + poseeLlave + ", posicion=" + posicion + "]";
	}
}

